package framework.classification.library;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;

import framework.AnnuaireCocola;
import framework.HierarchyCocola;
import framework.classification.entities.ClAbstractEntity;
import framework.classification.entities.ClComponent;
import framework.classification.entities.ClFunctionProvided;
import framework.classification.entities.ClFunctionRequired;
import framework.classification.entities.ClInterfaceProvided;
import framework.classification.entities.ClInterfaceRequired;
import framework.classification.entities.lattice.erca.LatticeFunctionProvidedErca;
import framework.classification.entities.lattice.erca.LatticeFunctionRequiredErca;
import framework.classification.entities.lattice.erca.LatticeInterfaceProvidedErca;
import framework.classification.entities.lattice.erca.LatticeInterfaceRequiredErca;
import framework.entity.ACLComponent;
import framework.entity.ACLEntity;
import framework.entity.ACLFunction;
import framework.entity.ACLInterface;
import framework.io.out.LatticeToImage;
import framework.io.out.TableToLatex;

public abstract class AbstractClassfication implements IClassificationDirectory,IOCLassificationDirectory{



	protected String outputFile;

	protected HierarchyCocola structure;

	protected AnnuaireCocola cocola;

	protected HashMap<String,ClFunctionProvided> functionsProvided;

	protected HashMap<String,ClFunctionRequired> functionRequired;

	protected HashMap<String, ClInterfaceRequired> interfacesRequired;

	protected HashMap<String, ClInterfaceProvided> interfacesProvided;

	protected HashMap<String, ArrayList<ClAbstractEntity>> components;

	protected HashMap<String, ACLComponent> componentsName;

	protected HashMap<String,ACLInterface> interfacesNames;


	protected ArrayList<ClAbstractEntity> clComponents;

	protected ArrayList<ClAbstractEntity> clInterfaces;

	protected ArrayList<ClAbstractEntity> clFunctions;


	protected String LatticeGeneration;


	public AbstractClassfication(HierarchyCocola structure,AnnuaireCocola coco,String latticeGeneration){

		this.cocola = coco;
		this.structure = structure;
		this.LatticeGeneration = latticeGeneration;

		functionsProvided = new HashMap<String, ClFunctionProvided>();
		functionRequired = new HashMap<String, ClFunctionRequired>();
		interfacesRequired = new HashMap<String, ClInterfaceRequired>();
		interfacesProvided = new HashMap<String, ClInterfaceProvided>();
		interfacesNames = new HashMap<String, ACLInterface>();
		components = new HashMap<String, ArrayList<ClAbstractEntity>>();
		componentsName = new HashMap<String, ACLComponent>();
		clComponents = new ArrayList<ClAbstractEntity>();
		clFunctions = new ArrayList<ClAbstractEntity>();
		clInterfaces = new ArrayList<ClAbstractEntity>();
		outputFile = cocola.getOuputPath()+getmode()+"/";
		initialiseOuptputPath();
		this.LatticeGeneration = latticeGeneration;

	}

	public AnnuaireCocola getCocola() {return cocola;}

	public String getOuputFile(){return outputFile;}
	public HierarchyCocola getStructure(){return structure;}

	//correspondance entre nom et definition
	public HashMap<String, ACLInterface> getInterfacesNames() {return interfacesNames;}
	public HashMap<String, ACLComponent> getComponentsNames() {return componentsName;}

	public ArrayList<ClAbstractEntity> getClComponents() {return clComponents;}

	public HashMap<String, ClFunctionProvided> getFunctionsProvided() {return functionsProvided;}
	public HashMap<String, ClFunctionRequired> getFunctionRequired() {return functionRequired;}
	public HashMap<String, ClInterfaceRequired> getInterfacesRequired() {return interfacesRequired;}
	public HashMap<String, ClInterfaceProvided> getInterfacesProvided() {return interfacesProvided;}

	public String getLatticeGeneration(){return LatticeGeneration;}
	public HashMap<String, ArrayList<ClAbstractEntity>> getComponents() {return components;}

	abstract public void computeComponentClassification();
	abstract public String getmode();


	public void initialiseOuptputPath(){
		File file = new File(cocola.getOuputPath()+getmode()+"/");
		if (!file.exists()) file.mkdir();
	}

	public void generateClEntityImage(ClAbstractEntity cl,String url,String format){
		cl.initialiseOutputFile();
		System.out.println(url+getOuputFile()+cl.getType()+"/Lattice/"+cl.getName()+"."+format);
		try {
			
			LatticeToImage.toFile(cl,url+getOuputFile()+cl.getType()+"/Lattice/"+cl.getName()+"."+format);
			
		} catch (IOException e) {
			System.err.println("Erreur generateComponentImage()");
		}

	};


	public void generateClEntityLatex(ClAbstractEntity cl,String url){
		cl.initialiseOutputFile();
		System.out.println(url+getOuputFile()+cl.getType()+"/Lattice/"+cl.getName()+".tex");
		
		try {
			TableToLatex.toFile(cl,url+getOuputFile()+cl.getType()+"/Latex/"+cl.getName());
		} catch (IOException e) {
			System.err.println("Erreur generateClEntityLatex");
		}


	}


	public void generateAllImage(String url,String format){
	
		for (ClAbstractEntity cl : clComponents){
			generateClEntityImage(cl,url+"/", format);
		}
		for (ClAbstractEntity cl : clInterfaces){
			generateClEntityImage(cl,url+"/",format);
		}
		for (ClAbstractEntity cl : clFunctions){
			generateClEntityImage(cl,url+"/",format);
		}
	}


	public void generateComponentsImage(String url,String format){
	
		for (ClAbstractEntity cl : clComponents){
			generateClEntityImage(cl,url+"", format);
		}
	}


	public void generateInterfacesImage(String url,String format){
		for (ClAbstractEntity cl : clInterfaces){
			generateClEntityImage(cl,url+"",format);
		}
	}

	public void generateFunctionsImage(String url,String format){
		for (ClAbstractEntity cl : clFunctions){
			generateClEntityImage(cl,url+"",format);
		}
	}
	
	public void generateAllImage(String format){

		for (ClAbstractEntity cl : clComponents){
			generateClEntityImage(cl,"", format);
		}
		for (ClAbstractEntity cl : clInterfaces){
			generateClEntityImage(cl,"", format);
		}
		for (ClAbstractEntity cl : clFunctions){
			generateClEntityImage(cl,"", format);
		}
	}

	public void generateAllLatex(){

		for (ClAbstractEntity cl : clComponents){
			generateClEntityLatex(cl,"");
		}
		for (ClAbstractEntity cl : clInterfaces){
			generateClEntityLatex(cl,"");
		}
		for (ClAbstractEntity cl : clFunctions){
			generateClEntityLatex(cl,"");
		}
	}

	public void generateComponentsLatex(String url){
		
		for (ClAbstractEntity cl : clComponents){
			generateClEntityLatex(cl,url+"/");
		}
	}


	public void generateInterfacesLatex(String url){
		for (ClAbstractEntity cl : clInterfaces){
			generateClEntityLatex(cl,url+"/");
		}
	}

	public void generateFunctionsLatex(String url){
		for (ClAbstractEntity cl : clFunctions){
			generateClEntityLatex(cl,url+"/");
		}
	}
	public void computeInterfaceClassification2(){

		//on partitionne les interfaces
		ArrayList<ArrayList<String>> part = getConnexeElement(
				structure.getInterfacesToFunctions(),
				structure.getFunctionsToInterfaces());

		//on recupere la definitions des interfaces
		int index = 0;
		for (ArrayList<String> inters : part){

			ArrayList<ACLInterface> listIn = new ArrayList<ACLInterface>();
			for (String s : inters){
				ACLInterface inter = new ACLInterface(s);
				inter.setFunctions(structure.getInterfacesFonction().get(s));
				listIn.add(inter);
			}
			//creation de la classification
			ClInterfaceProvided clpro = new ClInterfaceProvided(
					getLatticeGeneration()+ "_InterfacePro-"+index, 
					this,			   	
					new LatticeInterfaceProvidedErca(),
					listIn);
			//on creer les relations entre les interfaces et les fonctions
			clpro.computeRelation();
			//on genere le treillis
			clpro.computeLattice();
			clInterfaces.add(clpro);

			ClInterfaceRequired clreq = new ClInterfaceRequired(
					getLatticeGeneration()+"_InterfaceReq-"+index, 
					this,			   	
					new LatticeInterfaceRequiredErca(),
					listIn);
			index++;



			clreq.computeRelation();
			clreq.computeLattice();
			clInterfaces.add(clreq);

			//on recupere la definition des interfaces abstraites
			for (String intereq : clreq.getAbstractReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(intereq,ACLFunction.MODE2);
				interfacesRequired.put(aclinter.getName(),clreq);
				interfacesNames.put(aclinter.getName(),aclinter);
			}
			//on recupere la definition des interfaces reels
			for (String interreq : clreq.getRealReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interreq,ACLFunction.MODE2);
				interfacesRequired.put(aclinter.getName(),clreq);
				interfacesNames.put(aclinter.getName(),aclinter);
			}

			//on recupere la definition des interfaces abstraites
			for (String interpro : clpro.getAbstractReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interpro,ACLFunction.MODE2);
				interfacesProvided.put(aclinter.getName(),clpro);
				interfacesNames.put(aclinter.getName(),aclinter);
			}
			//on recupere la definition des interfaces reels
			for (String interpro : clpro.getRealReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interpro,ACLFunction.MODE2);
				interfacesProvided.put(aclinter.getName(),clpro);
				interfacesNames.put(aclinter.getName(),aclinter);
			}

		}



	}
	public void computeInterfaceClassification() {


		//on partitionne les interfaces
		ArrayList<ArrayList<String>> part = getConnexeElement(
				structure.getInterfacesProvided(),
				structure.getFonctionsProvidedInterfaces());


		int index=0;
		//on classifie les interfaces provided
		//pour chaque ensembles , on recupere la definition de chaque interfaces 
		for (ArrayList<String> inters : part){
			ArrayList<ACLInterface> listIn = new ArrayList<ACLInterface>();
			for (String s : inters){
				ACLInterface inter = new ACLInterface(s);
				inter.setFunctions(structure.getInterfacesFonction().get(s));
				listIn.add(inter);
			}

			ClInterfaceProvided cl = new ClInterfaceProvided(
					getLatticeGeneration()+"_InterfacePro-"+index, 
					this,			   	
					new LatticeInterfaceProvidedErca(),
					listIn);
			index++;

			cl.computeRelation();
			cl.computeLattice();
			clInterfaces.add(cl);

			//on recupere la definition des interfaces abstraites
			for (String interpro : cl.getAbstractReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interpro,ACLFunction.MODE2);
				interfacesProvided.put(aclinter.getName(),cl);
				interfacesNames.put(aclinter.getName(),aclinter);
			}
			//on recupere la definition des interfaces reels
			for (String interpro : cl.getRealReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interpro,ACLFunction.MODE2);
				interfacesProvided.put(aclinter.getName(),cl);
				interfacesNames.put(aclinter.getName(),aclinter);
			}
		}


		//classification des interfaces required
		//idem que ci-dessus
		part = getConnexeElement(
				structure.getInterfacesRequired(),
				structure.getFonctionsRequiredInterfaces());


		index=0;
		for (ArrayList<String> inters : part){
			ArrayList<ACLInterface> listIn = new ArrayList<ACLInterface>();
			for (String s : inters){
				ACLInterface inter = new ACLInterface(s);
				inter.setFunctions(structure.getInterfacesFonction().get(s));
				listIn.add(inter);
			}
			ClInterfaceRequired cl = new ClInterfaceRequired(
					getLatticeGeneration()+"_InterfaceReq-"+index,
					this,
					new LatticeInterfaceRequiredErca(),
					listIn);
			index++;
			cl.computeRelation();
			cl.computeLattice();
			clInterfaces.add(cl);
			for (String interreq : cl.getAbstractReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interreq,ACLFunction.MODE2);
				interfacesRequired.put(aclinter.getName(),cl);
				interfacesNames.put(aclinter.getName(),aclinter);
			}
			for (String interreq : cl.getRealReduced().keySet()){
				ACLInterface aclinter = new ACLInterface(interreq,ACLFunction.MODE2);
				interfacesRequired.put(aclinter.getName(),cl);
				interfacesNames.put(aclinter.getName(),aclinter);
			}
		}
	}

	public void computeFunctionClassfication2(){

		for (String s : structure.getFonctions().keySet()){


			ArrayList<ACLFunction> funcs = structure.getFonctions().get(s);

			ClFunctionProvided clpro = new ClFunctionProvided(getLatticeGeneration()+"_"+s+"-Provided", this,new LatticeFunctionProvidedErca(),funcs);
			clpro.computeRelation();
			clpro.computeLattice();
			clFunctions.add(clpro);
			for (String funcreel : clpro.getRealReduced().keySet()){
				functionsProvided.put(funcreel,clpro);
			}
			for (String funcabstract : clpro.getAbstractReduced().keySet()){
				functionsProvided.put(funcabstract,clpro);
			}


			ClFunctionRequired clreq = new ClFunctionRequired(getLatticeGeneration()+"_"+s+"-Required",this,new LatticeFunctionRequiredErca(),funcs);
			clreq.computeRelation();
			clreq.computeLattice();
			clFunctions.add(clreq);
			for (String funcreel : clreq.getRealReduced().keySet()){
				functionRequired.put(funcreel,clreq);
			}
			for (String funcabstract : clreq.getAbstractReduced().keySet()){
				functionRequired.put(funcabstract,clreq);
			}

		}
	}


	public void computeFunctionClassification() {
		for (String n : structure.getFonctionsRequired().keySet()){
			ArrayList<ACLFunction> funcs = structure.getFonctionsRequired().get(n);
				
			ClFunctionRequired cl = new ClFunctionRequired(getLatticeGeneration()+"_"+n+"-Required",this,new LatticeFunctionRequiredErca(),funcs);
			cl.computeRelation();
			cl.computeLattice();
			clFunctions.add(cl);
			for (String ent : cl.getRealReduced().keySet()){
				functionRequired.put(ent,cl);
			}

			for (String ent : cl.getAbstractReduced().keySet()){
				functionRequired.put(ent,cl);
			}
			
			

		}
		for (String n : structure.getFonctionsProvided().keySet()){
			ArrayList<ACLFunction> funcs = structure.getFonctionsProvided().get(n);
				
			
			
			ClFunctionProvided cl = new ClFunctionProvided(getLatticeGeneration()+"_"+n+"-Provided",this,new LatticeFunctionProvidedErca(),funcs);
			cl.computeRelation();
			cl.computeLattice();
			clFunctions.add(cl);
			for (String ent : cl.getRealReduced().keySet()){

				functionsProvided.put(ent,cl);
			}

			for (String ent : cl.getAbstractReduced().keySet()){

				functionsProvided.put(ent,cl);
			}
		}
	}



	//=================================================
	//=== Composantes connexes
	//=================================================
	// Entree : 2 hasmap formant un graphe biparti
	// Sortie : une liste de sous ensemble formant des composantes connexes


	public ArrayList<ArrayList<String>> getConnexeElement(
			HashMap<String, ArrayList<String>> part1,
			HashMap<String, ArrayList<String>> part2) {

		HashMap<String, Integer> hash = new HashMap<String, Integer>();
		Iterator<String> it = part1.keySet().iterator();
		int nbComposante = 1;

		while (it.hasNext()) {
			String s1 = it.next();
			// si le sommet n'est pas marquer
			if (!hash.containsKey(s1)) {
				// on fait un parcour en profondeur
				// on incremente le numero de la composante
				parcours(part1, part2, s1, hash, nbComposante);
				nbComposante++;
			}
		}

		ArrayList<ArrayList<String>> result = new ArrayList<ArrayList<String>>();
		// initialisation
		for (int i = 0; i < nbComposante - 1; i++) {
			result.add(new ArrayList<String>());
		}

		// on reconstruie les composantes connexes a partir de la hashmap
		Iterator<String> ithash = hash.keySet().iterator();
		while (ithash.hasNext()) {
			String s1 = ithash.next();
			result.get(hash.get(s1) - 1).add(s1);
		}

		return result;
	}

	public void parcours(HashMap<String, ArrayList<String>> part1,
			HashMap<String, ArrayList<String>> part2, String sommet,
			HashMap<String, Integer> hash, int i) {

		// si le sommet n'est pas marque
		if (!hash.containsKey(sommet)) {
			hash.put(sommet, i);
			for (String s2 : part1.get(sommet)) {

				for (String s1 : part2.get(s2)) {

					parcours(part1, part2, s1, hash, i);
				}
			}
		}
	}

	//==========================================================
	//=== Fin Composantes Connexes
	//==========================================================


	//==========================================================
	//=== Substitution
	//==========================================================
	public ArrayList<ACLFunction> getSubstituteFunctionRequired(String name){
		ArrayList<ACLFunction> result = new ArrayList<ACLFunction>();
		if (!functionRequired.containsKey(name)){
			System.err.println("ERREUR::"+" Nothing Required Substitution find for "+name+"\n");
			return new ArrayList<ACLFunction>();
		}
		ArrayList<String> subs = functionRequired.get(name).getSubstituteElement(name);
		for (String sub :subs ){
			ACLFunction aclfunc = new ACLFunction(sub,ACLFunction.MODE2);
			result.add(aclfunc);
		}

		return result;
	}


	public ArrayList<ACLFunction> getSubstituteFunctionProvided(String name){
		ArrayList<ACLFunction> result = new ArrayList<ACLFunction>();

		if (!functionsProvided.containsKey(name)){
			System.err.println("ERREUR::"+" Nothing Provided Substitution find for "+name);
			return new ArrayList<ACLFunction>();
		}

		ArrayList<String> subs = functionsProvided.get(name).getSubstituteElement(name);
		for (String sub :subs ){
			ACLFunction aclfunc = new ACLFunction(sub,ACLFunction.MODE2);
			result.add(aclfunc);
		}

		return result;

	}
	public ArrayList<ACLInterface> getSubstituteInterfaceRequired(String name){

		ArrayList<ACLInterface> result = new ArrayList<ACLInterface>();

		if (!interfacesRequired.containsKey(name)){
			System.err.println("ERREUR::"+" Nothing Required Interface Substitution find for "+name);
			return new ArrayList<ACLInterface>();
		}

		ArrayList<String> subs = interfacesRequired.get(name).getSubstituteElement(interfacesNames.get(name).toString());
		for (String sub :subs ){
			ACLInterface aclinter = new ACLInterface(sub,ACLFunction.MODE2);
			result.add(aclinter);
		}

		return result;


	}
	public ArrayList<ACLInterface> getSubstituteInterfaceProvided(String name){

		ArrayList<ACLInterface> result = new ArrayList<ACLInterface>();
		
		if (!interfacesProvided.containsKey(name)){
			System.err.println("ERREUR::"+" Nothing Provided Interface Substitution find for "+name);
			return new ArrayList<ACLInterface>();
		}
		
		ArrayList<String> subs = interfacesProvided.get(name).getSubstituteElement(interfacesNames.get(name).toString());
		for (String sub :subs ){
			ACLInterface aclinter = new ACLInterface(sub,ACLFunction.MODE2);
			result.add(aclinter);
		}

		return result;


	}



	public ArrayList<ACLComponent> getSubstituteComponent(String name,ClAbstractEntity cl) {

		ArrayList<ACLComponent> result = new ArrayList<ACLComponent>();
		if (!components.containsKey(name)){
			System.err.println("ERREUR::"+" Nothing Component Substitution find for "+name);
			return new ArrayList<ACLComponent>();
		}
		
		ArrayList<String> subs = cl.getSubstituteElement(componentsName.get(name).toString());
		for (String sub :subs ){
			ACLComponent aclcompo = new ACLComponent(sub,ACLFunction.MODE2);
			result.add(aclcompo);
		}
		return result;
	}
	
	public ArrayList<ACLComponent> getSubstitutionComponent(String name){
		
		ArrayList<ACLComponent> result = new ArrayList<ACLComponent>();
	
		if (!components.containsKey(name)){
			System.err.println("ERREUR::"+" Nothing Component Substitution find for "+name);
			return new ArrayList<ACLComponent>();
		}
		for (ClAbstractEntity cl : components.get(name)){
			
			ArrayList<String> subs = cl.getSubstituteElement(componentsName.get(name).toString());
			for (String sub :subs ){
				ACLComponent aclcompo = new ACLComponent(sub,ACLFunction.MODE2);
				result.add(aclcompo);
					
				
			}
			
			
			
		}
		
		
		return result;
	}
	//==============================================================
	//=== FIN Substitution
	//==============================================================



	//==============================================================
	//=== Connection
	//==============================================================

	public ArrayList<ClAbstractEntity> getConnection(ClInterfaceProvided cl) {
		ArrayList<ClAbstractEntity> sub = new ArrayList<ClAbstractEntity>();

		return null;
	}


	public ArrayList<ClAbstractEntity> getConnection(ClInterfaceRequired cl) {
		ArrayList<ClAbstractEntity> sub = new ArrayList<ClAbstractEntity>();

		return null;
	}


}
